using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Threading;
using System.Xml.Linq;
using Coding4Fun.Phone.Controls;
using ImageTools;
using ImageTools.IO.Png;
using Lasalara.Controls;
using Lasalara.Model;
using Microsoft.Phone.Net.NetworkInformation;
using Microsoft.Phone.Shell;
using Rectangle = System.Windows.Shapes.Rectangle;

namespace Lasalara.ViewModel
{
    public class GlobalViewModel
    {

        /// <summary>
        /// Displays the progress bar.
        /// </summary>
        /// <param name="text">Progress bar text</param>
        /// <param name="bar">Progress bar to display</param>
        public static void ShowProgressBar(ProgressIndicator bar, string text)
        {
            bar.IsIndeterminate = true;
            bar.Text = text;
            bar.IsVisible = true;
        }

        /// <summary>
        /// Hides the indeterminate progress bar.
        /// </summary>
        public static void HideProgressBar(ProgressIndicator bar)
        {
            bar.IsIndeterminate = false;
            bar.Text = "";
            bar.IsVisible = false;
        }

        /// <summary>
        /// Removes progress from live tile when user logs out.
        /// </summary>
        public static void RemoveLiveTileProgress()
        {
            var tile = ShellTile.ActiveTiles.First();
            var newBackContent = "Are" + Environment.NewLine;
            newBackContent += "you" + Environment.NewLine;
            newBackContent += "done?" + Environment.NewLine;

            var newTileData = new StandardTileData
                              {
                                  Count = 0,
                                  BackTitle = "log in",
                                  BackContent = newBackContent
                              };
            tile.Update(newTileData);
        }

        /// <summary>
        /// Update Live Tile progress in background thread.
        /// </summary>
        public static void UpdateLiveTileProgress(User currentUser)
        {
            // Find tile
            var tileToFind = ShellTile.ActiveTiles.First();
            var newTileData = new StandardTileData();
            var bw = new BackgroundWorker();
            bw.DoWork += (sender, args) =>
            {
                double totalQuestionsDownloaded =
                    currentUser.DownloadedUserBooks.SelectMany(
                        book => book.Chapters.Where(x => x.IsDownloaded)).Sum(
                            chapter => chapter.Questions.Count);
                double totalQuestionsAnswered =
                    currentUser.DownloadedUserBooks.SelectMany(
                        book => book.Chapters.Where(x => x.IsDownloaded)).Sum(
                            chapter => chapter.Questions.Count(x => x.AnsweredDate > DateTime.Now));
                var val = 0.0;
                if (!totalQuestionsAnswered.Equals(0))
                {
                    val = totalQuestionsAnswered / totalQuestionsDownloaded * 100;
                }
                if (val > 100)
                {
                    val = 100;
                }

                var newBackContent = AppResources.Answered + ": " + totalQuestionsAnswered + Environment.NewLine;
                newBackContent += AppResources.Questions + ": " + totalQuestionsDownloaded + Environment.NewLine;
                var progress = (int)val + "%";
                if (val > 0 && val < 1)
                {
                    progress = "<1%";
                }

                if (val >= 100) val = 0;

                
                var uri = new Uri("Background.png", UriKind.Relative);
                var info = Application.GetResourceStream(uri);
                BitmapImage bmpImage = null;

                Deployment.Current.Dispatcher.BeginInvoke(() =>
                {
                    bmpImage = new BitmapImage {CreateOptions = BitmapCreateOptions.None, UriSource = uri};
                    bmpImage.ImageOpened += (s, e) =>
                    {
                        var bmp = new WriteableBitmap((BitmapImage) s);

                        // determine Count
                        var count = new TextBlock
                                        {
                                            Foreground = new SolidColorBrush(Colors.Black),
                                            FontFamily = new FontFamily("PhoneFontFamilySemiBold"),
                                            FontSize = 28,
                                            Text = progress
                                        };

                        var countPosition = new TranslateTransform {X = 173 - count.ActualWidth - 10, Y = 22};

                        bmp.Render(count, countPosition);
                        bmp.Invalidate();

                        using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                        {
                            var filename = "/Shared/ShellContent/LiveTileFront.jpg";
                            using (var st = new IsolatedStorageFileStream(filename, FileMode.Create, FileAccess.Write, store))
                            {
                                bmp.SaveJpeg(st, 173, 173, 0, 100);
                            }
                        }
                    };
                });

               
                
                newTileData = new StandardTileData
                {
                    Title = "",
                    BackgroundImage = new Uri("isostore:/Shared/ShellContent/LiveTileFront.jpg", UriKind.Absolute),
                    Count = 0,
                    //BackTitle = "",
                    BackTitle = progress,
                    BackBackgroundImage = new Uri("isostore:/Shared/ShellContent/LiveTileBack.jpg", UriKind.Absolute),
                    BackContent = newBackContent
                    //BackContent = ""
                };                

            };
            bw.RunWorkerAsync();
            bw.RunWorkerCompleted += (sender, args) => tileToFind.Update(newTileData);
                

        }

        /// <summary>
        /// Shuffles given list
        /// </summary>
        /// <param name="list">List to shuffle</param>
        public static List<T> Randomize<T>(IEnumerable<T> list)
        {
            var rnd = new Random();
            return list.OrderBy((item) => rnd.Next()).ToList();
        }

        /// <summary>
        /// Checks if the requested sponsor image exists on the phone and that the version number matches. 
        /// If the version doesn't match or the image does not exist on the phone, download an xml file containing all the links
        /// and download the image specified with the "imageName" property.
        /// </summary>
        /// <param name="imageName">The name of the sponsor image to download ("logo1, logo2, etc..")</param>
        /// <param name="callback">The method that is launched when the download has finished.</param>
        public static void DownloadSponsorImage(string imageName, Action<WriteableBitmap, string> callback)
        {
            if (!DeviceNetworkInformation.IsNetworkAvailable)
            {
                // If there is no internet and sponsor is already downloaded display that.
                if (IsImageAvailable(imageName))
                {
                    callback(GetSponsorImage(imageName), imageName);
                }
                else
                {
                    var defaultImg = new BitmapImage(new Uri("../Images/nike-justdoit.png", UriKind.Relative));
                    callback(new WriteableBitmap(defaultImg), "");
                }
            }
            else
            {
                var wc = new WebClient();
                wc.DownloadStringCompleted += (sender, args) =>
                            {
                                if (args.Error == null)
                                {
                                    var xml = XDocument.Parse(args.Result);
                                    var newVersion = (from e in xml.Elements("links")
                                                      select (int)e.Element(imageName + "_version")).First();

                                    var oldSponsorVersion = GetAppSettings(imageName + "_version");
                                    // If the same version for the required image exists in the phone, return that instead.
                                    if (oldSponsorVersion == null || (int)oldSponsorVersion < newVersion)
                                    {
                                        var img =
                                            (from e in xml.Elements("links")
                                             select
                                                 new BitmapImage(new Uri((string) e.Element(imageName), UriKind.Absolute)))
                                                .First();

                                        SetAppSetting(imageName + "_version", newVersion);
                                        img.CreateOptions = BitmapCreateOptions.BackgroundCreation;
                                        // When the image is downloaded save it to storage and then call callback method.
                                        img.ImageOpened += (s, a) =>
                                                               {
                                                                   SaveSponsorImage(img, imageName);
                                                                   callback(GetSponsorImage(imageName), imageName);
                                                               };
                                    }
                                    else
                                    {
                                        callback(GetSponsorImage(imageName), imageName);
                                    }
                                    
                                }
                            };
                wc.DownloadStringAsync(new Uri(AppResources.SponsorLinkFileUrl, UriKind.Absolute));
            }
        }

        /// <summary>
        /// Downloads the link when clicking on a sponsor banner.
        /// </summary>
        /// <param name="callback">The method to call when the sponsor link has finished downloading.</param>
        public static void DownloadSponsorLink(Action<Uri> callback)
        {
            if (DeviceNetworkInformation.IsNetworkAvailable)
            {
                var wc = new WebClient();
                wc.DownloadStringCompleted += (sender, args) =>
                {
                    if (args.Error == null)
                    {
                        var xml = XDocument.Parse(args.Result);
                        var link = 
                            (from e in xml.Elements("links") 
                                select new Uri((string)e.Element("link"), UriKind.Absolute)).First();

                        SetAppSetting("sponsorLink", link);
                        callback(link);
                    }
                };
                wc.DownloadStringAsync(new Uri(AppResources.SponsorLinkFileUrl, UriKind.Absolute));
            }
            else
            {
                // If there is no internet and sponsor is already downloaded display that.
                var sponsorLink = GetAppSettings("sponsorLink") as Uri;
                if (sponsorLink != null)
                {
                    callback(sponsorLink);
                }
                else
                {
                    ShowToast(AppResources.Error, AppResources.InternetProblem);
                }
            }
        }
        /// <summary>
        /// Saves sponsor image as .PNG to phone storage.
        /// </summary>
        /// <param name="img">The bitmap that needs to be converted and saved</param>
        /// <param name="imgName">The name to give the image that is saved.</param>
        private static void SaveSponsorImage(BitmapSource img, string imgName)
        {
            var isf = IsolatedStorageFile.GetUserStoreForApplication();
            if (imgName == "banner1" || imgName == "banner2" || imgName == "banner3")
            {
                using (var writer = new StreamWriter(new IsolatedStorageFileStream(imgName + ".jpg", FileMode.Create, FileAccess.Write, isf)))
                {
                    var wb = new WriteableBitmap(img);
                    wb.SaveJpeg(writer.BaseStream, wb.PixelWidth, wb.PixelHeight, 0, 100);
                    writer.Close();
                }
            }
            else
            {
                using (var writer = new StreamWriter(new IsolatedStorageFileStream(imgName + ".png", FileMode.Create, FileAccess.Write, isf)))
                {
                    var encoder = new PngEncoder();
                    var wb = new WriteableBitmap(img);
                    encoder.Encode(wb.ToImage(), writer.BaseStream);
                }
            }
        }

        /// <summary>
        /// Checks if an image with the supplied name already exists in storage
        /// </summary>
        /// <param name="imageName">The name of the image to search for.</param>
        private static bool IsImageAvailable(string imageName)
        {
            using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (isf.FileExists(imageName + ".png") || isf.FileExists(imageName + ".jpg"))
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Fetches a .PNG image from the phone storage and returns it as a writeable bitmap.
        /// </summary>
        /// <param name="imageName">The name of the image to fetch from storage.</param>
        private static WriteableBitmap GetSponsorImage(string imageName)
        {
            using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (imageName == "banner1" || imageName == "banner2" || imageName == "banner3")
                {
                    var downloadStream = isf.OpenFile(imageName + ".jpg", FileMode.Open, FileAccess.Read);
                    using (var reader = new StreamReader(downloadStream))
                    {
                        var img = new BitmapImage();
                        img.SetSource(reader.BaseStream);
                        var wb = new WriteableBitmap(img);
                        reader.Close();
                        return wb;
                    }
                }
                else
                {
                    var downloadStream = isf.OpenFile(imageName + ".png", FileMode.Open, FileAccess.Read);
                    using (var reader = new StreamReader(downloadStream))
                    {
                        var decoder = new PngDecoder();
                        var img = new ExtendedImage();
                        decoder.Decode(img, reader.BaseStream);
                        reader.Close();
                        return img.ToBitmap();
                    }
                }
            }
        }

        /// <summary>
        /// Determines if the logo image has to be downloaded and fades it in when it has loaded.
        /// </summary>
        public static void SetLogo(ImageBrush logo, Rectangle rectangle, string logoName)
        {
            DownloadSponsorImage(logoName, (image, name) => 
            {
                logo.ImageSource = image;
            });
        }

        /// <summary>
        /// Hides or shows the cancel web request button
        /// </summary>
        public static void ShowCancelButton(bool isVisible, UIElement grid)
        {
            var fadeStoryBoard = new Storyboard();
            var fadeAnimation = new DoubleAnimation()
            {
                Duration = new Duration(TimeSpan.FromMilliseconds(500))
            };
            fadeStoryBoard.Children.Add(fadeAnimation);

            Storyboard.SetTarget(fadeAnimation, grid);
            Storyboard.SetTargetProperty(fadeAnimation, new PropertyPath("Opacity"));

            if (isVisible)
            {
                grid.Visibility = Visibility.Visible;
                fadeAnimation.From = 0;
                fadeAnimation.To = 1;
                fadeStoryBoard.Begin();
            }
            else
            {
                fadeAnimation.From = 1;
                fadeAnimation.To = 0;
                fadeStoryBoard.Begin();
                fadeStoryBoard.Completed += (sender, args) => grid.Visibility = Visibility.Collapsed;
            }
        }

        /// <summary>
        /// Shows toast notification
        /// </summary>
        /// <param name="title">message title</param>
        /// <param name="message">message content</param>
        public static void ShowToast(string title, string message)
        {
            var toast = new ToastPrompt
                            {
                                Title = title,
                                Message = message,
                                TextOrientation = Orientation.Vertical,
                                TextWrapping = TextWrapping.Wrap
                            };
            toast.Show();
        }

        /// <summary>
        /// Encrypts users password
        /// </summary>
        /// <param name="data">User's password</param>
        public static byte[] EncryptData(string data)
        {
            var dataBytes = Encoding.UTF8.GetBytes(data);
            var encryptedPasswordData = ProtectedData.Protect(dataBytes, null);
            return encryptedPasswordData;
        }

        /// <summary>
        /// Save data to localstorage
        /// </summary>
        /// <param name="key">The key to save</param>
        /// <param name="value">The value of the key</param>
        public static void SetAppSetting(string key, object value)
        {
            var appSettings = IsolatedStorageSettings.ApplicationSettings;
            try
            {
                if (appSettings.Contains(key)) { appSettings[key] = value; }
                else { appSettings.Add(key, value); }
            }
            catch (ArgumentException ex)
            {
                MessageBox.Show(AppResources.ErrorSavingData + ":" + ex, AppResources.Error, MessageBoxButton.OK);
            }
        }

        /// <summary>
        /// Fetch data from localstorage. Returns null if setting not found.
        /// </summary>
        /// <param name="key">The key of the value to fetch</param>
        public static object GetAppSettings(string key)
        {
            var appSettings = IsolatedStorageSettings.ApplicationSettings;
            return appSettings.Contains(key) ? appSettings[key] : null;
        }

        /// <summary>
        /// Validate if the email is syntactically correct
        /// </summary>
        /// <param name="email">The email to validate</param>
        public static bool ValidateEmail(string email)
        {
            const string strRegex = @"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}" +
                                    @"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +
                                    @".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
            var re = new Regex(strRegex);
            return re.IsMatch(email);
        }

        /// <summary>
        /// Gets the current user from IsolatedStorageSettings. Returns null if no user is found.
        /// </summary>
        public static User GetCurrentUser()
        {
            var userName = GetAppSettings("currentUser");
            if (userName != null)
            {
                var currentUser = GetAppSettings(userName.ToString());
                if (currentUser != null)
                {
                    return currentUser as User;
                }
            }
            return null;
        }

        /// <summary>
        /// Saves current user to storage
        /// </summary>
        public static void SaveUser(User user)
        {
            SetAppSetting(user.Email, user);
        }
    }
}